Introduzione all'assembly
il computer
il
processore
la memoria
gli
interrupt
Il computer ha come scopo quello di eseguire dei programmi, cioè delle sequenze di istruzioni. Un programma per essere eseguito deve innanzitutto essere caricato in memoria, dopodichè il processore si occupa di svolgere, una dopo l'altra, tutte le istruzioni che lo compongono. Abbiamo visto quindi due delle componenti fondamentali del PC: Il processore e la memoria.
Il
processore è l'unita centrale di elaborazione (CPU)
del computer ed è costutuito da un'unità aritmetico-logica (ALU), un'unità di governo e
da una memoria locale formata da diversi registri.
L'unità aritmetico logica è un dispositico
capace di eseguire le operazioni aritmetiche, logiche e di confronto su dati prelevati
dalla memoria. In poche parole è una calcolatrice a cui di volta in volta vengono forniti
l'operatore e gli operandi, e che produce un risultato.
L'unità di governo invece ha il compito di
interpretare l'istruzione da eseguire e comandare di conseguenza l'ALU.
Entrambi questi componenti colloquiano con la
memoria locale, che abbiamo detto essere formata da dispositivi di memorizzazione detti
registri.
i registri generici sono quattro, ognuno è progettato per eseguire in particolare una determinata funzione, ma possono essere usati (quasi) a piacimento.
Come tutti gli altri registri della CPU, i registri generici sono a 16 bit (cioè ognuno è composto da 2 byte di memoria). Durante la scrittura di un programma assembly possiamo fare riferimento anche esclusivamente al byte alto o a quello basso di ognuno di questi registri:
Un'altra importante serie di registri sono
i registri segmento. Essi sono utilizzati dal processore per localizzare una determinata
cella di memoria: contengono infatti l'indirizzo della prima cella del relativo segmento
(i segmenti e l'indirizzamento della memoria verranno tratato tra poche righe). I registri
segmento sono:
CS code segment
DS data segment
ES extra segment
SS stack segment
Infine, i registri puntatori sono in
qualche modo complementari a quelli segmento: mentre questi ultimi vengono usati per
individuare i segmenti da 64Kb all'interno della memoria, i 5 registri puntatori servono
per individuare, attraverso di uno l'indirizzo di spiazzamento (in inglese offset),
una locazione all'interno di uno specifico segmento. Questi 5 registri sono:
IP (istruction pointer) | fornisce l'offset della cella di memoria (all'interno del segmento individuato da CS) che contiene la prossima istruzione da eseguire |
SP (stack pointer) | punta alla testa dello stack nel segmento individuato da SS |
BP (base pointer) | puntatore utilizzato per le operazione nello stack nel segmento individuato da SS |
SI (source index) | puntatore usato per lo spiazzamento negli indirizzamenti all'interno del segmento dati individuato da DS |
DI (destination index) | puntantore usato per lo spiazzamento negli indirizzamenti all'interno del segmento dati individuato da DS o nel segmento extra individuato da ES |
la memoria è
la capacita di conservare informazioni. All'interno del computer il dispositivo che si
occupa di questo è la RAM (Random Access Memory), in cui vengono posti i programmi in
esecuzione con i relativi dati. La RAM del computer è divisa in celle da un bye
ciascuna,e ad ogni cella è associato un indirizzo, che può andare da 0 fino al numero di
celle presenti. Anche tale numero è conservato in forma binaria, e la sua lunghezza è di
20 bit. Con 20 bit possiamo contare fino a 220 = 1 milione di celle circa
(1Mbyte infatti è la massima memoria indirizzabile dall'i8086), per chiarimenti
consultare l'appendice sistemi di numerazione.
Durante la scrittura di un programma però si
presenta un problema: come facciamo ad esprimere un indirizzo a 20 bit usando solo
registri a 16 bit? Per farlo si divide la memoria in segmenti di 64Kbyte ciascuno (con 16
bit possiamo contare infatti fino a 64K celle), e si indirizza una cella attraverso due
registri: in uno viene posto l'indirizzo di partenza del segmento, e nell'altro l'offset,
cioè la distanza della cella da esso. I due registri coinvolti nel caso la cella
contenga un'istruzione sono la coppia CS:IP, nel caso di dati su usa in genere la
coppia di registri DS:SI o DS:DI.
Nel primo registro è indicato il segmento
(non a caso il registro usato si chiama registro segmento), nel secondo l'offset.
|
In figura, per indirizzare la cella B che contiene codice da eseguire, si utilizzano CS:IP, dove CS contiene l'indirizzo di partenza del segmento (la cella A) e IP la distanza di B da esso (l'indirizzo di B meno l'indirizzo di A) cioè l'offset.
Durante l'esecuzione di un programma, il
registro Code Segment (CS) indica sempre il segmento di memoria dove è contenuto il
codice da eseguire, il Data Segment (DS) quello dove sono contenuti i dati e così via.
I registri della CPU e la RAM si scambiano
continuamente dati, attraverso un BUS dati a 16 bit (cioè in grado di trasferire 2 byte
di informazioni in un unico passaggio), mentre lo scambio degli indirizzo avviene
attravarso un BUS dedicato (bus indirizzi) a 20 bit.
|
gli
interrupt sono uno strumento usato dalla CPU per interfacciarsi alle altre
componenti del computer. Spesso una periferica ha bisogno di richiamare l'attenzione della
CPU al verificarsi di un evento. Ad esempio, la tastiera deve rendere noto al sistema che
un tasto è stato premuto. Per farlo utilizza un interrupt, o interruzione. In pratica,
forza la CPU ad abbandonare temporaneamente quello che stava facendo e ad occuparsi della
gestione dell'evento che si è verificato.
Il processore, quando arriva una rischiesta
di interrupt (un flag viene portato a 1) deve innanzitutto riconoscere quale periferica lo
ha richiesto. Ogni periferica ha il suo indirizzo (livello di interrupt), e lo comunica
alla CPU attraverso un bus apposito ogni volta che richiede un interrupt. La CPU riconosce
tale indirizzo, sospende l'operazione in corso (in pratica salva lo stato di tutti i
registri in una memoria apposita) e localizza l'handler di tale interruzione. Una
volta eseguito, ripristina lo stato originario e prosegue in ciò che stava elaborando
prima della chiamata. L'handler di un interrupt è uno spezzone di codice, caricato in
memoria all'avvio del computer, che serve a gestire quell'evento. Nelle prime celle di
memoria è presente una tabella, in cui ad ogni livello di interrupt è associato
l'indirizzo del relativo handler.
Il sistema è in grado di eseguire un solo
interrupt alla volta. Se durante l'esecuzione di un handler viene segnalato un'altro
interrupt, la chiamata viene ignorata.
|
Una periferica, quando deve segnalare
un evento alla CPU tramite un interrupt, pone il suo identificativo nel BUS dedicato, e
mette a 1 IF (interrupt flag). La CPU riconosce la periferica leggendo l'identificativo
sul BUS, localizza nella tabella degli interrupt l'handler giusto e, dopo aver salvato il
suo stato, lo esegue. Durante questo passaggio eventuali altre chiamate di interrupt
verranno ignorate. Infine ripristina il suo stato originario e riprende il processo che
aveva sospeso.
Gli handler sono piccoli programmi che fanno
parte del sistema operativo. Tuttavia, possiamo anche definirne di nostri. Basta scrivere
il codice relativo, metterlo in memoria come TSR (Terminate and Stay Resident: una
volta eseguito, non viene scaricato dalla memoria) e porre il suo indirizzo nella tabella
degli interrupt.
Esistono due tipi di chiamate di interrupt: sincrone
ed asincrone. Le chiamate asincrone sono quelle che vengono generate dalle
periferiche in modo imprevedibile (possono arrivare in qualsiasi momento), mentre quelle
sincrone sono quelle che il programmatore genera con un'apposita istruzione assembly
(l'istruzione INT).